package com.atguigu.gmall.activity.receiver;

import com.atguigu.gmall.activity.mapper.SeckillGoodsMapper;
import com.atguigu.gmall.activity.service.SeckillGoodsService;
import com.atguigu.gmall.common.constant.MqConst;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.util.DateUtil;
import com.atguigu.gmall.model.activity.SeckillGoods;
import com.atguigu.gmall.model.activity.UserRecode;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.rabbitmq.client.Channel;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.Exchange;
import org.springframework.amqp.rabbit.annotation.Queue;
import org.springframework.amqp.rabbit.annotation.QueueBinding;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.util.Collection;
import java.util.Date;
import java.util.List;

@Slf4j
@Component
public class SeckillReceiver {

    @Autowired
    private SeckillGoodsMapper seckillGoodsMapper;

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private SeckillGoodsService seckillGoodsService;

    /**
     * 监听消息实现商品预热
     * @param msg
     * @param message
     * @param channel
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_TASK_1,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
            key = {MqConst.ROUTING_TASK_1}
    ))
    public void importDataToRedis(String msg, Message message, Channel channel){
        try {
            //  扫描当天秒杀的商品导入redis. new date();
            //  审核状态 status = 1; 商品剩余数量：stock_count
            //  select * from seckill_goods where date_format(start_time,'%Y-%m-%d') = date_format(now(),'%Y-%m-%d') and status = '1' and stock_count > 0;
            LambdaQueryWrapper<SeckillGoods> seckillGoodsLambdaQueryWrapper = new LambdaQueryWrapper<>();
            seckillGoodsLambdaQueryWrapper.eq(SeckillGoods::getStatus,"1");
            seckillGoodsLambdaQueryWrapper.gt(SeckillGoods::getStockCount,0);
            //  date_format(start_time,'%Y-%m-%d') = date_format(now(),'%Y-%m-%d')
            seckillGoodsLambdaQueryWrapper.apply("DATE_FORMAT(start_time,'%Y-%m-%d') =" + "'" + DateUtil.formatDate(new Date()) + "'");
            //        QueryWrapper<SeckillGoods> seckillGoodsQueryWrapper = new QueryWrapper<>();
            //        seckillGoodsQueryWrapper.eq("status","1").gt("stock_count",0).eq("date_format(start_time,'%Y-%m-%d')",DateUtil.formatDate(new Date()));
            //        List<SeckillGoods> seckillGoodsList1= seckillGoodsMapper.selectList(seckillGoodsQueryWrapper);
            List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(seckillGoodsLambdaQueryWrapper);
            //  将数据放入缓存
            for (SeckillGoods seckillGoods : seckillGoodsList) {
                //  缓存的数据类型： hash  hset key field value; hget key field;
                String seckillKey = RedisConst.SECKILL_GOODS;
                //  判断缓存中是否存在该数据
                Boolean exist = this.redisTemplate.opsForHash().hasKey(seckillKey, seckillGoods.getSkuId().toString());
                if (exist){
                    //  不能覆盖：
                    log.info("当前商品已存在!{}",seckillGoods.getSkuId());
                    continue;
                }
                //  将商品已经存储到缓存
                this.redisTemplate.opsForHash().put(seckillKey,seckillGoods.getSkuId().toString(),seckillGoods);
                //  存储一个商品的剩余库存数量：存储 redis-list 队列中. 10
                for (Integer i = 0; i < seckillGoods.getStockCount(); i++) {
                    //  lpush key value rpush key value  rpop key lpop key;   lpush seckill:stock:skuId skuId ---> 为什么不适用 incr key
                    this.redisTemplate.opsForList().leftPush(RedisConst.SECKILL_STOCK_PREFIX+seckillGoods.getSkuId(),seckillGoods.getSkuId().toString());
                }

                //  初始化状态位： publish seckillpush 24:1
                this.redisTemplate.convertAndSend("seckillpush",seckillGoods.getSkuId()+":1");
            }
        } catch (Exception e) {
            log.error("商品预热失败.");
            throw new RuntimeException(e);
        }
        //  签收消息
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);

    }

    /**
     * 监听预下单队列中的信息
     * @param userRecode
     * @param message
     * @param channel
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_SECKILL_USER,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_SECKILL_USER),
            key = {MqConst.ROUTING_SECKILL_USER}
    ))
    public void seckillUser(UserRecode userRecode, Message message ,Channel channel){
        try {
            //  判断
            if (userRecode!=null){
                log.info("处理队列中的数据信息。");
                //  业务处理：
                seckillGoodsService.seckillUser(userRecode);
            }
        } catch (Exception e) {
            log.error("处理队列中的数据处理失败."+e.getMessage());
            throw new RuntimeException(e);
        }
        //  手动签收
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    /**
     * 监听秒杀减库存消息
     * @param skuId
     * @param message
     * @param channel
     */
    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_SECKILL_STOCK,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_SECKILL_STOCK),
            key = {MqConst.ROUTING_SECKILL_STOCK}
    ))
    public void seckillStock(Long skuId, Message message ,Channel channel){
        try {
            //  判断
            if (skuId!=null){
                log.info("监听秒杀减库存。");
                //  业务处理：
                seckillGoodsService.seckillStock(skuId);
            }
        } catch (Exception e) {
            log.error("监听秒杀减库存."+e.getMessage());
            throw new RuntimeException(e);
        }
        //  手动签收
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

    @SneakyThrows
    @RabbitListener(bindings = @QueueBinding(
            value = @Queue(value = MqConst.QUEUE_TASK_18,durable = "true",autoDelete = "false"),
            exchange = @Exchange(value = MqConst.EXCHANGE_DIRECT_TASK),
            key = {MqConst.ROUTING_TASK_18}
    ))
    public void clearData(String msg, Message message, Channel channel){
        try {
            //  删除数据.   审核状态=1 and endTime<new Date();
            LambdaQueryWrapper<SeckillGoods> seckillGoodsLambdaQueryWrapper = new LambdaQueryWrapper<>();
            seckillGoodsLambdaQueryWrapper.eq(SeckillGoods::getStatus,"1");
            seckillGoodsLambdaQueryWrapper.le(SeckillGoods::getEndTime,new Date());
            //  查询当天秒杀结束的商品.
            List<SeckillGoods> seckillGoodsList = seckillGoodsMapper.selectList(seckillGoodsLambdaQueryWrapper);
            //  循环遍历
            if (!CollectionUtils.isEmpty(seckillGoodsList)){
                for (SeckillGoods seckillGoods : seckillGoodsList) {
                    //  删除商品的剩余库存;
                    this.redisTemplate.delete(RedisConst.SECKILL_STOCK_PREFIX+seckillGoods.getSkuId());
                }
            }
            //  删除秒杀商品key
            this.redisTemplate.delete(RedisConst.SECKILL_GOODS);
            //  删除预下单的key
            this.redisTemplate.delete(RedisConst.SECKILL_ORDERS);
            //  删除真正秒杀key
            this.redisTemplate.delete(RedisConst.SECKILL_ORDERS_USERS);

            //  更改数据库
            //  设置更新内容
            SeckillGoods seckillGoods = new SeckillGoods();
            seckillGoods.setStatus("0");
            this.seckillGoodsMapper.update(seckillGoods,seckillGoodsLambdaQueryWrapper);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        //  手动确认
        channel.basicAck(message.getMessageProperties().getDeliveryTag(),false);
    }

}
